package com.own.business.login.core.aspect;

import com.own.aop.login.LoginApiAop;
import com.own.component.common.login.util.LoginInfoUtil;
import com.own.component.log.login.util.LogLoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * LoginApiAspect
 *
 * @author chenxueli
 * @date 2023-12-12 15:20:06
 */
@Slf4j
@Aspect
@Component
public class LoginApiAspect {

    private static final SpelExpressionParser SPEL_EXPRESSION_PARSER = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private static final DefaultParameterNameDiscoverer DEFAULT_PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
    @Resource
    private LogLoginUtil logLoginUtil;
    @Resource
    private LoginInfoUtil loginInfoUtil;

    @Pointcut("@annotation(com.own.aop.login.LoginApiAop)")
    public void aspect() {
    }

    @Before("aspect() && @annotation(data)")
    public void before(JoinPoint point, LoginApiAop data) {
        var account = StringUtils.isNotBlank(data.account()) ? getSpelValue(data.account(), point) : null;
        // 设置基础信息
        loginInfoUtil.type().set(data.type());
        loginInfoUtil.account().set(account);
        loginInfoUtil.code().set(null);
        if (data.isCheck()) {
            logLoginUtil.check(account, data.type());
        }
    }

    /**
     * 获取spel表达式的值
     *
     * @param expressionStr 表达式
     * @param point         切点
     * @return 值
     */
    private String getSpelValue(String expressionStr, JoinPoint point) {
        var methodSignature = (MethodSignature) point.getSignature();
        var paramNames = DEFAULT_PARAMETER_NAME_DISCOVERER.getParameterNames(methodSignature.getMethod());
        if (paramNames == null) {
            return "";
        }
        var expression = SPEL_EXPRESSION_PARSER.parseExpression(expressionStr);
        var context = new StandardEvaluationContext();
        Object[] args = point.getArgs();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        var value = expression.getValue(context);
        return value == null ? "" : value.toString();
    }

    @After("aspect() && @annotation(data)")
    public void after(JoinPoint point, LoginApiAop data) {
        // 从point中获取返回值
    }

    /**
     * 记录异常信息
     *
     * @param point 切点
     * @param error 异常信息
     */
    @AfterThrowing(pointcut = "aspect()", throwing = "error")
    public void throwing(JoinPoint point, Throwable error) {
        logLoginUtil.fail(error.getMessage());
    }

}
